/*
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

/*
 *
 *
 *
 *
 *
 * Written by Doug Lea with assistance from members of JCP JSR-166
 * Expert Group and released to the public domain, as explained at
 * http://creativecommons.org/publicdomain/zero/1.0/
 */

package java.util.concurrent.atomic;

/**
 * AtomicStampedReference维护一个对象引用和一个整数“stamp”，它可以自动更新。
 *
 * <p>注意:这个实现通过创建内部对象来维护stampedreference，这些对象表示“boxed”[reference, integer]对。
 *
 * @since 1.5
 * @author Doug Lea
 * @param <V> The type of object referred to by this reference
 */
public class AtomicStampedReference<V> {

	// 你可能想问，反正静态内部类Pair是只给自己用的，何必将Pair定义成泛型类呢，完全可以如下定义。
	// 一来这样就无法使用静态函数of了，构造器是私有的只能通过这个静态函数访问构造器（注释中有解释）；
	// 二来Pair对象并不需要作为成员内部类而存在，即Pair不需要持有外部类的引用。
    private static class Pair<T> {
    	// reference成员保存你的引用型对象，stamp成员则保存你对象的版本号
    	// 为了让Pair对象不可变，让两个域都是final的。你没有办法修改Pair对象的成员，所以只能通过静态函数重新构造一个Pair对象。这一点很重要。
        final T reference;
        final int stamp;
        private Pair(T reference, int stamp) {
            this.reference = reference;
            this.stamp = stamp;
        }
        static <T> Pair<T> of(T reference, int stamp) {
            return new Pair<T>(reference, stamp);
        }
    }

    // AtomicStampedReference有一个volatile的Pair<V>成员，这样，保证了pair成员的可见性。
    private volatile Pair<V> pair;

    /**
     * 使用给定的初始值创建一个新的AtomicStampedReference。
     *
     * @param initialRef the initial reference
     * @param initialStamp the initial stamp
     */
    public AtomicStampedReference(V initialRef, int initialStamp) {
        pair = Pair.of(initialRef, initialStamp);
    }

    /**
     * 返回引用的当前值。
     * 
     * getReference/getStamp都是通过volatile的成员pair获得的，所以具有可见性。
     *
     * @return the current value of the reference
     */
    public V getReference() {
        return pair.reference;
    }

    /**
     * 返回戳记的当前值。
     *
     * @return the current value of the stamp
     */
    public int getStamp() {
        return pair.stamp;
    }

    /**
     * 返回引用和戳记的当前值。典型用法是{@code int[1] holder; ref = v.get(holder); }.
     * 
     * get函数是为了“一次性”获得存储对象和版本号。对象通过返回值，版本号通过int数组。
     * 
     * 因为int作为参数只能值传递，而int数组是一个对象，可以引用传递，所以要使用int数组。由于只需要得到版本号，所以数组大小大于1就可以了。
     * 
     * 当然，该函数是复合操作，不具有原子性。
     *
     * @param stampHolder an array of size of at least one.  On return,
     * {@code stampholder[0]} will hold the value of the stamp.
     * @return the current value of the reference
     */
    public V get(int[] stampHolder) {
        Pair<V> pair = this.pair;
        stampHolder[0] = pair.stamp;
        return pair.reference;
    }

    /**
     * 如果当前引用==期望的引用，并且当前戳记等于期望的戳记，则原子地将引用和戳记的值设置为给定的更新值。
     *
     * <p>可能会错误地失败，并且不提供排序保证，所以它很少是compareAndSet的合适选择。
     *
     * @param expectedReference the expected value of the reference
     * @param newReference the new value for the reference
     * @param expectedStamp the expected value of the stamp
     * @param newStamp the new value for the stamp
     * @return {@code true} if successful
     */
    public boolean weakCompareAndSet(V   expectedReference,
                                     V   newReference,
                                     int expectedStamp,
                                     int newStamp) {
        return compareAndSet(expectedReference, newReference,
                             expectedStamp, newStamp);
    }

    /**
     * 如果当前引用==期望的引用，并且当前戳记等于期望的戳记，则原子地将引用和戳记的值设置为给定的更新值。
     *
     * @param expectedReference the expected value of the reference
     * @param newReference the new value for the reference
     * @param expectedStamp the expected value of the stamp
     * @param newStamp the new value for the stamp
     * @return {@code true} if successful
     */
    public boolean compareAndSet(V   expectedReference,
                                 V   newReference,
                                 int expectedStamp,
                                 int newStamp) {
        Pair<V> current = pair;
        return
        	// 首先expectedReference == current.reference判断旧引用，和当前的是否一样；expectedStamp == current.stamp判断旧版本，和当前的是否一样。	
            expectedReference == current.reference &&
            expectedStamp == current.stamp &&
            // (newReference == current.reference && newStamp == current.stamp)判断 新引用和新版本号，是否和当前的相同，一般情况下，不会满足此判断的。
            ((newReference == current.reference &&
              newStamp == current.stamp) ||
            // 既然上一条不满足，则调用casPair，最终调用到Unsafe的compareAndSwapObject。	 return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);	
            // 注意，每次执行此函数时，都会重新构造Pair对象，这一点很关键。
            // 这保证调用compareAndSwapObject函数时，参数expected和x肯定两个不同的对象，
            // 就算最开始的expectedReference与newReference一样，且expectedStamp与newStamp一样，也会构造出一个新对象，当然这由于短路或不会执行到的。
            // 完全有可能，从expectedStamp == current.stamp到casPair(current, Pair.of(newReference, newStamp))期间，线程被切换走了，
            // 	切换的时候pair成员变量已经被修改了，等切换回来，current这个局部变量就不和成员pair相等了，自然casPair会失败。		
             casPair(current, Pair.of(newReference, newStamp)));
    }

    /**
     * 无条件地设置引用和戳记的值。
     * 
     * 这是用来无条件设置的函数。因为不需要保持旧值是否相同。
     * 如果 新引用和新版本号 和 当前的 一样，那么不需要更新pair成员。
     *
     * @param newReference the new value for the reference
     * @param newStamp the new value for the stamp
     */
    public void set(V newReference, int newStamp) {
        Pair<V> current = pair;
        if (newReference != current.reference || newStamp != current.stamp)
            this.pair = Pair.of(newReference, newStamp);
    }

    /**
     * 如果当前引用==期望的引用，则原子地将戳记的值设置为给定的更新值。
     * 这个操作的任何给定调用可能会假的失败(返回false)，但是当，当前值持有期望的值，
     * 并且没有其他线程也试图设置值时，重复调用将最终成功。
     * 
     * 此函数只更新版本号，不更新引用对象。
     * 只需保证旧引用和当前的相同，casPair函数保证了 只有current局部变量与当前pair成员是同一个对象时，才更新。
     *
     * @param expectedReference the expected value of the reference
     * @param newStamp the new value for the stamp
     * @return {@code true} if successful
     */
    public boolean attemptStamp(V expectedReference, int newStamp) {
        Pair<V> current = pair;
        return
            expectedReference == current.reference &&
            (newStamp == current.stamp ||
             casPair(current, Pair.of(expectedReference, newStamp)));
    }

    // Unsafe mechanics

    private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
    
    // 获得pair域的偏移量。
    private static final long pairOffset =
        objectFieldOffset(UNSAFE, "pair", AtomicStampedReference.class);

    private boolean casPair(Pair<V> cmp, Pair<V> val) {
    	// 最终调用Unsafe的compareAndSwapObject方法时，是不关心版本号的。compareAndSwapObject只关心是不是同一个对象。（但这样不会造成问题）
    	// 虽然根据上一条，感觉可能会有问题。但是由于静态内部类Pair每次都会新构造对象出来，
    	// 即使T reference, int stamp两个参数完全一样，也会构造出两个不同的对象，所以最终调用的compareAndSwapObject不关心版本号也没有关系。
    	// 综上，版本号是在Unsafe的CAS操作上进行的附加判断，准备的说，是先判断版本号，再通过CAS操作判断对象。
        return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
    }

    static long objectFieldOffset(sun.misc.Unsafe UNSAFE,
                                  String field, Class<?> klazz) {
        try {
            return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field));
        } catch (NoSuchFieldException e) {
            // 将异常转换为相应的错误
            NoSuchFieldError error = new NoSuchFieldError(field);
            error.initCause(e);
            throw error;
        }
    }
}
